﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DiscreteEventModel {

    using Common;

    public partial class DiscreteEvent<TContext> {

        public abstract class Agent : DiscreteEvent<TContext>, ITokenizable<int>, IComparer<DiscreteEvent<TContext>> {

            internal DiscreteTime _stateTime;
            internal PriorityQueue<DiscreteEvent<TContext>> _eventQueue;
            int ITokenizable<int>.Token { get; set; }

            protected Agent(IProcess process) {
                this._stateTime = (process as EngineProcess)._time;
                this._eventQueue = PriorityQueue<DiscreteEvent<TContext>>.Create(this);
            }

            public int Compare(DiscreteEvent<TContext> event1, DiscreteEvent<TContext> event2) {
                int diff = event2._time.CompareTo(event1._time);
                if(diff == 0) {
                    if(event1 == this) {
                        return 1;
                    } else if(event2 == this) {
                        return -1;
                    }
                }
                return diff;
            }

            public DiscreteTime ActionTime {
                get {
                    DiscreteEvent<TContext> topEvent;
                    return this._eventQueue.Peek(out topEvent) ? topEvent._time : DiscreteTime.Infinity;
                }
            }

            protected internal abstract void Synchronize(double timeStep);
            protected internal abstract void Input(DiscreteEvent<TContext> @event);
            protected internal abstract void Output(IProcess process);
            protected internal abstract double Commit(TContext context);
            protected internal abstract void Dispose(TContext context);
        }

        public abstract class CausalAgent : Agent {

            private Queue<DiscreteEvent<TContext>> _eventTrace;
            private double _eventTraceLength;

            protected CausalAgent(IProcess process, double eventTraceLength)
                : base(process) {
                this._eventTraceLength = eventTraceLength;
                this._eventTrace = new Queue<DiscreteEvent<TContext>>();
            }

            protected abstract void OnInput(DiscreteEvent<TContext> @event);
            protected abstract void OnOutput(IProcess process, IEnumerable<DiscreteEvent<TContext>> eventTrace);

            sealed protected internal override void Input(DiscreteEvent<TContext> @event) {
                this.OnInput(@event);
                this._eventTrace.Enqueue(@event);
            }

            sealed protected internal override void Output(IProcess process) {
                while(this._eventTrace.Count > 0 && (this._stateTime - this._eventTrace.Peek()._time) > this._eventTraceLength) {
                    this._eventTrace.Dequeue();
                }
                this.OnOutput(process, this._eventTrace);
            }
        }
    }
}
